package com.wmapp.utils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.dom.DOMElement;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import com.wmapp.DomainConvertException;
import com.wmapp.utils.processor.NameProcessor;

public abstract class DomainUtils {
	private static Log log =LogFactory.getLog(DomainUtils.class);
	
	@SuppressWarnings("unchecked")
	public static Object element2Domain(Element element,Class domainClass){
		return element2Domain(element, domainClass,null);
	}
	
	@SuppressWarnings("unchecked")
	public static Object element2Domain(Element element,Class domainClass,NameProcessor nameProcessor){
		Object ret=null;
		try {
			ret=domainClass.newInstance();
			
			for (Iterator iterator = element.elementIterator(); iterator.hasNext();) {
				Element e = (Element) iterator.next();
				String fieldName=nameProcessor!=null?nameProcessor.processor(e.getName()):e.getName();
				
				if(fieldName.equals("interface")) //interface is a java keyword,so we alias it as "iface"
					fieldName="iface";
				
				Field field=ReflectionUtils.findField(domainClass, fieldName);
				
				if(field!=null	//if element has no corresponding field in domain object it will be ignored
						&&field.getType().getName().equals("java.lang.String")){//just parse String field
					String setMethodName = SimpleStringUtils.join("set",
							StringUtils.capitalize(fieldName));
					Method setMethod = domainClass.getMethod(setMethodName,
							new Class[] { String.class });
					String val = e.getStringValue();
					if (val != null && !val.equals("")) {// ignore empty string
						setMethod.invoke(ret, new Object[] { val });
					}
				}
			}
		} catch (Exception e) {
			log.error(SimpleStringUtils.join("element ",element.toString()," to ",domainClass.toString()," fail."), e);
			throw new DomainConvertException(e.getMessage());
		}
		return ret;
	}
	
	public static Element domain2Element(String elementName,Object obj,String[] fields){
		return domain2Element(elementName, obj, fields,null);
	}
	
	/**
	 * Convert domain object to xml element.
	 * @param elementName element name
	 * @param obj Domain object
	 * @param fields Domain object fields
	 * @param nodeNames Element node names,null or String array which length same as fields length
	 * @return
	 */
	public static Element domain2Element(String elementName,Object obj,String[] fields,String[] nodeNames){
		Element element=null;
		
		try {
			element=new DOMElement(elementName);
			Class clazz=obj.getClass();
			for (int i = 0; i < fields.length; i++) {
				String field=fields[i];
				Method getMethod=clazz.getMethod(SimpleStringUtils.join("get",StringUtils.capitalize(field)), null);
				String val=(String)getMethod.invoke(obj, null);
				if(val!=null){
					if(nodeNames!=null)
						element.addElement(nodeNames[i]).setText(val);
					else
						element.addElement(field).setText(val);
				}
			}
		} catch (Exception e) {
			log.error(SimpleStringUtils.join("domain ",obj.toString()," to element ",elementName," fail."), e);
			throw new DomainConvertException(e.getMessage());
		}
		
		return element;
	}
	
	public static Map domain2Map(Object obj,String[] fields){
		Map map=new HashMap<String, String>();
		
		try {
			Class clazz=obj.getClass();
			for (int i = 0; i < fields.length; i++) {
				String field=fields[i];
				Method getMethod=clazz.getMethod(SimpleStringUtils.join("get",StringUtils.capitalize(field)), null);
				String val=(String)getMethod.invoke(obj, null);
				if(val!=null){
					map.put(field, val);
				}
			}
		} catch (Exception e) {
			log.error(SimpleStringUtils.join("domain ",obj.toString()," to map fail."), e);
			throw new DomainConvertException(e.getMessage());
		}
		
		return map;
	}
	
	public static List xml2List(String xml,String domainXPath,Class domainClazz){
		List list=new ArrayList();
		
		if(StringUtils.hasText(xml)){
			try {
				Document document = DocumentHelper.parseText(xml);
				List nodes=document.selectNodes(domainXPath);
				for (Iterator iterator = nodes.iterator(); iterator.hasNext();) {
					Element element = (Element) iterator.next();
					if(element.hasContent())//if element content is empty,ignored
						list.add(element2Domain(element, domainClazz));
				}
			} catch (Exception e) {
				log.error("xml convert to list fail.", e);
				throw new DomainConvertException(e.getMessage());
			}
		}
		
		return list;
	}
}
